package com.region.http.client.core;

import com.region.common.utils.ReflectUtils;
import com.region.http.client.model.RequestMeta;
import com.region.http.client.pool.AsyncParameterCallable;
import com.region.http.client.pool.RegionPoolExecutor;
import com.region.http.client.process.ExecutorProcess;
import com.region.http.client.utils.LockUtils;

import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;

/**
 * @author 26225
 * @date 2024/8/31 16:51
 * @description
 */
public class RuntimeParseHandler extends MetaParseHandler {

    protected RegionPoolExecutor poolExecutor;
    /**
     * The single instance execute processing
     */
    protected ExecutorProcess singleProcess;
    /**
     * The multi instance execute processing
     */
    protected ExecutorProcess multiProcess;

    /**
     * Execute the proxy method and return the request result value
     *
     * @param method
     * @param args
     * @return
     */
    public Object invokeObject(Method method, Object[] args) throws Throwable {
        RequestMeta requestMeta = metaData.get(method);
        if (requestMeta.isAsynchronous()) {
            Class<?> returnClazz = ReflectUtils.transform(requestMeta.getReturnType());
            AsyncParameterCallable parameterCallable = getParameterCallable(requestMeta, args);
            if (void.class.isAssignableFrom(returnClazz)) {
                poolExecutor.execute(() -> execute(requestMeta, args));
            } else if (CompletableFuture.class.isAssignableFrom(returnClazz)) {
                CompletableFuture completableFuture = poolExecutor.submitByCompletable(() -> execute(requestMeta, args));
                if (parameterCallable != null) {
                    return completableFuture.handleAsync((data, t) -> {
                        parameterCallable.apply(data);
                        return data;
                    }, poolExecutor.getPoolExecutor());
                }
                return completableFuture;
            } else if (Future.class.isAssignableFrom(returnClazz)){
                Future submit = poolExecutor.submit(() -> execute(requestMeta, args));
                if (parameterCallable != null) {
                    poolExecutor.execute(() -> parameterCallable.apply(submit));
                    return null;
                }
                return submit;
            } else {
                if (parameterCallable != null) {
                    poolExecutor.submitByCompletable(() -> execute(requestMeta, args)).handleAsync((data, t) -> {
                        parameterCallable.apply(data);
                        return data;
                    }, poolExecutor.getPoolExecutor());
                } else {
                    poolExecutor.execute(() -> execute(requestMeta, args));
                }
            }
            return null;
        } else {
            return execute(requestMeta, args);
        }
    }

    /**
     * Get Asynchronous callable
     *
     * @param requestMeta
     * @param args
     * @return
     */
    public AsyncParameterCallable getParameterCallable(RequestMeta requestMeta, Object[] args) {
        RequestMeta.MetaParameter metaParameter = requestMeta.getMetaParameter();
        if (metaParameter == null || metaParameter.getCallableIndex() == null) {
            return null;
        }
        return (AsyncParameterCallable) args[metaParameter.getCallableIndex().intValue()];
    }

    public Object execute(RequestMeta requestMeta, Object[] args) throws Throwable{
        LockUtils.getReadLock().lock();
        try {
            if (requestMeta.isStatus()) {
                return multiProcess.doExecute(requestMeta, args, Registers.getInstance().getHttpExecutor(requestMeta.getExecutor()));
            } else {
                return singleProcess.doExecute(requestMeta, args, Registers.getInstance().getHttpExecutor(requestMeta.getExecutor()));
            }
        } finally {
            LockUtils.getReadLock().unlock();
        }
    }
}
